<?php
namespace app\common\library;

use think\Cache;

class RedisReentrantLock
{
    protected $cache;
    protected $lockKey;
    protected $clientId;

    public function __construct($lockKey)
    {
        $this->cache = Cache::init(); // 获取ThinkPHP5的缓存驱动（Redis）
        $this->lockKey = $lockKey;
        $this->clientId = uniqid('tp5_', true); // 生成唯一客户端ID
    }

    /**
     * 获取锁（支持可重入）
     * @param int $ttl 锁超时时间（秒）
     * @param int $retryTimes 重试次数
     * @param int $retryDelay 重试间隔（毫秒）
     * @return bool
     */
    public function lock($ttl = 10, $retryTimes = 3, $retryDelay = 100)
    {
        $redis = $this->cache->handler(); // 获取底层Redis对象（phpredis/predis）
        $lua = <<<LUA
local key = KEYS[1]
local clientId = ARGV[1]
local ttl = tonumber(ARGV[2])

if redis.call('EXISTS', key) == 0 then
    redis.call('HMSET', key, clientId, 1)
    redis.call('EXPIRE', key, ttl)
    return 1
end

if redis.call('HEXISTS', key, clientId) == 1 then
    redis.call('HINCRBY', key, clientId, 1)
    redis.call('EXPIRE', key, ttl)
    return 1
end

return 0
LUA;

        $retry = 0;
        while ($retry < $retryTimes) {
            $result = $redis->eval($lua, [$this->lockKey, $this->clientId, $ttl], 1);
            if ($result) {
                return true;
            }
            usleep($retryDelay * 1000);
            $retry++;
        }
        return false;
    }

    /**
     * 释放锁
     * @return bool
     */
    public function unlock()
    {
        $redis = $this->cache->handler();
        $lua = <<<LUA
local key = KEYS[1]
local clientId = ARGV[1]

if redis.call('EXISTS', key) == 0 then
    return 1
end

if redis.call('HEXISTS', key, clientId) == 1 then
    local count = redis.call('HINCRBY', key, clientId, -1)
    if count <= 0 then
        redis.call('DEL', key)
    end
    return 1
end

return 0
LUA;

        return (bool)$redis->eval($lua, [$this->lockKey, $this->clientId], 1);
    }
}
